import traceback
from copy import copy, deepcopy
try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

from config import *

LOG = make_logging("kxml")

class KXml:
    """ 用于处理GUI与xml的信息交互 """
    def __init__(self, path):
        self._path = path

    def _widget_type_check(self, elem_widget):
        """
        return: dict_widget
        """
        if elem_widget.attrib["type"] not in Set_Widgets_type:
            raise Exception("No such widget type 【%s】, ERROR object 【%s】" % (elem_widget.attrib["type"], elem_widget.tag))

        set_attr_unknown = set(elem_widget.attrib.keys()) - Dict_Widget_attrtype_unique[elem_widget.attrib["type"]].keys() - Set_Widgets_attrib_common
        if set_attr_unknown:
            raise Exception("No such widget attribute 【%s】" % set_attr_unknown)

        dict_widget = {}
        for item in elem_widget.attrib:
            # 对于widget标签，根据 Dict_Widget_attribs 转换数据格式；
            # if item in Dict_Widget_attrtype_common:
            #     attr_type = Dict_Widget_attrtype_common[item]
            # else:
            #     attr_type = Dict_Widget_attrtype_unique[elem_widget.attrib["type"]][item]
            # obj_namedtuple = switch_str2namedtuple(attr_type, elem_widget.attrib[item])
            dict_widget[item] = elem_widget.attrib[item]
        return dict_widget

    def _parse_xml_struct(self, elem_root):
        # 解析本层文本
        if elem_root.tag not in Set_Xml_type:  # like "page", "widget"
            raise Exception("Unknown element tag 【%s】" % elem_root.tag)

        set_attr_unknown = set(elem_root.attrib.keys()) - Dict_Xml_describe[elem_root.tag]
        if set_attr_unknown:
            raise Exception("No such widget attribute 【%s】" % set_attr_unknown)

        # 无差别解析
        dict_tmp = {}
        if elem_root.tag == "widget":
            self._widget_type_check(elem_root)
        for item in elem_root.attrib:
            dict_tmp[item] = elem_root.attrib[item]

        # 分类统计
        if elem_root.tag == "window":
            self._dict_kivinfo = dict_tmp
        elif elem_root.tag == "plugin":
            self._list_plugininfo.append(dict_tmp)
        elif elem_root.tag == "page":
            self._list_pageinfo.append(dict_tmp)
        elif elem_root.tag == "widget":
            # 将当前widget加入到_list_pageinfo列表最后一项的dict中
            dict_page = self._list_pageinfo[-1]
            if "widgets" not in dict_page:
                dict_page["widgets"] = []
            dict_page["widgets"].append(dict_tmp)

        # 解析结构，逐层递归
        for elem in elem_root:
            LOG.debug("解析XML::递归 --> " + str(elem))
            self._parse_xml_struct(elem)

    def load_file(self):
        """ 载入图元文件信息，
        return: dict_all_elem 存储对象，而非字符串
        """
        self._dict_kivinfo = {}
        self._list_pageinfo = []
        self._list_plugininfo = []
        try:
            self.tree = ET.parse(self._path)
            self.elem_root = self.tree.getroot()
            self._parse_xml_struct(self.elem_root)

        except Exception as e:
            LOG.error(e)
            traceback.print_exc()

        self._dict_all_elem = {
            "window": self._dict_kivinfo,
            "pages": self._list_pageinfo,
            "plugins": self._list_plugininfo
        }
        return deepcopy(self._dict_all_elem)

    def dump_file(self, dict_new):
        """ 用于将当前图元信息写入文件 """
        self.cmp_dictinfo_with_new(dict_new)
        pass

    def modify_xml(self, str_tag_path, tuple_attr):
        # for example: str_tag_path="./page_name/widget_name"
        # self.elem_root.find
        pass


    def cmp_list_with_dict(self, list_new, list_old, path, key="name"):
        if list_new == list_old:
            return None
        else:
            set_list_new = self._set_from_list_of_dict(list_new, key)
            set_list_old = self._set_from_list_of_dict(list_old, key)
            # 处理新增项；
            for item in (set_list_new - set_list_old):
                pass

            # 处理减少项：
            for item in (set_list_old - set_list_new):
                pass

            # 处理变更项：




    def cmp_dictinfo_with_new(self, dict_new):
        # 对比【window】节点
        if not self._cmp_dict(self._dict_kivinfo, dict_new["window"]):
            LOG.info("Update the 【window】 tag text")

        # 对比【plugins】节点

        # 对比【pages】节点
        self._cmp_pages_list(dict_new["pages"], self._list_pageinfo)


    def _cmp_pages_list(self, list_new:list, list_old:list):
        set_common_pages = self._cmp_list_of_dict(list_new, list_old)
        if set_common_pages is None:
            LOG.debug("【pages】对比相同")
        else:
            LOG.info("Update the 【pages】 tag text")
            # checking the next leval -> page
            for dict_page in list_old:
                dict_page_new = self._find_dict_from_list_by_name(list_new, dict_page["name"])
                self._cmp_page_dict(dict_page_new, dict_page)

    def _cmp_page_dict(self, dict_new:dict, dict_old:dict):
        if self._cmp_dict(dict_new, dict_old):
            LOG.debug("【page::%s】对比相同" % dict_new["name"])
            # same dict, go on checking the next leval -> widgets
            self._cmp_widgets_list(dict_new["widgets"], dict_old["widgets"])
        else:
            LOG.debug("Update the 【page::%s】 tag text" % dict_new["name"])

    def _cmp_widgets_list(self, list_new:list, list_old:list):
        if self._cmp_list_of_dict(list_new, list_old):
            LOG.debug("【widgets】对比相同")
            # same list, go on checking the next leval -> widget
            for dict_widget in list_old:
                dict_widget_new = self._find_dict_from_list_by_name(list_new, dict_widget["name"])
                self._cmp_widget_dict(dict_widget_new, dict_widget)

    def _cmp_widget_dict(self, dict_new:dict, dict_old:dict):
        if self._cmp_dict(dict_new, dict_old):
            LOG.debug("【widget::%s】对比相同" % dict_new["name"])
        else:
            LOG.debug("Update the 【widget::%s】 tag text" % dict_new["name"])

    def _find_dict_from_list_by_name(self, list_pages:list, page_name:str):
        for dict_page in list_pages:
            if dict_page["name"] == page_name:
                return dict_page

    def _cmp_dict(self, dict_new:dict, dict_old:dict):
        set_old = set(dict_old.keys())
        set_new = set(dict_new.keys())
        if dict_old != dict_new:
            # 判断数量
            # if (set_old | set_new)) - (set_old & set_new):
            if set_old - set_new:
                LOG.debug("去除项：" + str(set_old - set_new))
            if set_new - set_old:
                LOG.debug("增加项：" + str(set_new - set_old))
            for key in (set_old & set_new):
                if dict_new[key] != dict_old[key]:
                    LOG.debug("不同项：" + str(key))
            return False
        else:
            return True

    def _cmp_list_of_dict(self, list_new:list, list_old:list, key:str="name"):
        """ 对比两个list，list内容为dict，比较dict['name'] """
        set_old = self._set_from_list_of_dict(list_old, key)
        set_new = self._set_from_list_of_dict(list_new, key)
        if list_old != list_new:
            # 判断数量
            # if (set_old | set_new)) - (set_old & set_new):
            if set_old - set_new:
                LOG.debug("【list】去除项：" + str(set_old - set_new))
            if set_new - set_old:
                LOG.debug("【list】增加项：" + str(set_new - set_old))
            # 修改项，在子类中调用
            return (set_old & set_new)
        else:
            return None

    def _set_from_list_of_dict(self, lst:list, key:str):
        """ 从一个list中，取dict[key]生成set """
        set_name = set()
        for item in lst:
            set_name.add(item[key])
        return set_name